package server.db;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;

import server.config.ServerConfig;

public class ConnectionPool {
	
	/**
	 * 
	 */
	private static Logger logger = Logger.getLogger(ConnectionPool.class);

	private Semaphore sema = new Semaphore(ServerConfig.getInstance()
			.getConnectionPoolSize());

	private String databaseUrl;
	private String userName;
	private String password;

	private ConcurrentLinkedQueue<Connection> connectionPool = new ConcurrentLinkedQueue<Connection>();

	public ConnectionPool(String databaseUrl, String userName, String password) {
		this.databaseUrl = databaseUrl;
		this.userName = userName;
		this.password = password;
		initialize();
	}

	private void initialize() {
		// Here we can initialize all the information that we need
		initializeConnectionPool();
	}

	private void initializeConnectionPool() {
		while (!checkIfConnectionPoolIsFull()) {
			
			Connection con = createNewConnectionForPool();
			if(con == null) {
				return;
			}
			connectionPool.add(con);
		}
	}

	private boolean checkIfConnectionPoolIsFull() {

		// Check if the pool size
		if (connectionPool.size() < ServerConfig.getInstance()
				.getConnectionPoolSize()) {
			return false;
		}
		return true;
	}

	// Creating a connection
	private Connection createNewConnectionForPool() {

		Connection connection = null;
		try {
			Class.forName("com.mysql.jdbc.Driver");
			connection = DriverManager.getConnection(databaseUrl, userName,
					password);
		} catch (SQLException sqle) {
			throw new RuntimeException(sqle);
		} catch (ClassNotFoundException cnfe) {
			System.err.println("ClassNotFoundException: " + cnfe);
			return null;
		}
		return connection;
	}

	public Connection getConnectionFromPool() {
		Connection connection = null;

		// Check if there is a connection available. There are times when all
		// the connections in the pool may be used up
		try {
			if (sema.tryAcquire(5, TimeUnit.SECONDS)) {
				connection = connectionPool.poll();

				return connection;
			} else {
				System.out
						.println("Neue Connection erstellt! Sollte nicht vorkommen!");
				returnConnectionToPool(createNewConnectionForPool());
				return getConnectionFromPool();
			}
		} catch (Exception e1) {
			logger.error("", e1);
		}
		return null;

	}

	public boolean connectionOk() {

		boolean connectionOk = false;
		try {
			for (int i = 0; i < ServerConfig.getInstance()
					.getConnectionPoolSize(); i++) {
				// wenn 5 seks keine connection verfügbar, wird false
				// zurückgegeben
				if (sema.tryAcquire(5000, TimeUnit.MILLISECONDS)) {

					Connection connection = connectionPool.poll();
					try {
						if (!connection.isValid(10) || connection.isClosed()) {
							System.out
									.println("Mysql Fehler Connection nicht Ok");
							return false;
						}
						connectionOk = true;
					} catch (SQLException e) {
						logger.error("", e);
						return false;
					}
					returnConnectionToPool(connection);
				} else {
					returnConnectionToPool(createNewConnectionForPool());
				}
			}
		} catch (InterruptedException e1) {
		}
		return connectionOk;
	}

	public void returnConnectionToPool(Connection connection) {

		if (connection != null) {
			connectionPool.add(connection);
			sema.release();
		}
	}
}
